#include <QtGui>
#include "graphwidget.h"
#include "agent.h"
#include "node.h"
#include <QGraphicsScene>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <sstream>

#include "traffic_sim.h"

using namespace Traffic_Sim;

Traffic_Graph * graph;

const string nodos[] = {"Las Tapias", "Humboldt", "Pie del Llano", "Casa Blanca", "Alto Prado", "Mercado", "Medicina", "Santa Juana", "Glorias Patrias", "Santo Niño", "Viaducto", "Santa Barbara", "Mc Donalds" };

void create_street(string src_node_desc, string dest_node_desc, int delay, int capacity, string street_name) 
{
  Traffic_Node_Info info1(src_node_desc);
  Traffic_Graph::Node * n1 = graph->search_node(info1);
  if (n1 == NULL) 
  {
    n1 = graph->insert_node(info1);
  }

  Traffic_Node_Info info2(dest_node_desc);
  Traffic_Graph::Node * n2 = graph->search_node(info2);
  if (n2 == NULL)
  {
    n2 = graph->insert_node(info2);
  }
  Traffic_Arc_Info info_arc(street_name, delay, capacity);
  cout << "Creada calle " << info_arc.description << " desde " << info1.description  << " hasta " << info2.description << endl;
  graph->insert_arc(n1, n2, info_arc);
}

void semaphore_update(void * semaphore_ptr)
{
  Traffic_Semaphore * semaphore = static_cast<Traffic_Semaphore *>(semaphore_ptr);
 // cout << "Actualización en tricolor " << semaphore->get_description() << endl;
  for (int i = 0; i < semaphore->get_arc_count(); i++)
  {
    Semaphore_Color color = semaphore->get_color_to_show(i);
    Traffic_Arc * arc = semaphore->get_arc(i);
    string color_str;
    switch (color)
    {
      case Green:
        color_str = "Verde";
        break;
      case Yellow:
        color_str = "Amarillo";
        break;
      case Red:
        color_str = "Rojo";
        break;
    }
   // cout << "Para el arco " << arc->get_info().description << " el color del tricolor es " << color_str << endl;
  }
}

void create_semaphore_agent(int id, string node_desc, int arc_count,  
    long activation_time, string* arc_nodes_desc )
{
  stringstream agent_name_strm(stringstream::out) ;
  agent_name_strm << "Tricolor en " << node_desc;

  Traffic_Node_Info node_info(node_desc);
  Traffic_Node * node = graph->search_node(node_info);

  Traffic_Semaphore * semaphore;

  string agent_name = agent_name_strm.str();

  if (arc_count == 3)
  {
    semaphore = new Three_Way_Traffic_Semaphore(id, agent_name, node, arc_count);
  } 
  else 
  {
    semaphore = new Traffic_Semaphore(id, agent_name, node,  arc_count);
  }

  node->get_info().semaphore = semaphore;

  for (int i = 0; i < arc_count; i++)
  {
    Traffic_Node_Info dest_node_info(arc_nodes_desc[i]);
    Traffic_Node * dest_node = graph->search_node(dest_node_info);

    Traffic_Arc * arc = graph->search_arc(node, dest_node);
    semaphore->set_arc_index(arc, i);
  }
  semaphore->set_update_callback(&semaphore_update, semaphore);

  Traffic_Agent_Impl::create_agent_in_node(semaphore, graph, node, activation_time);
  cout << "Creado tricolor en " << node_desc << endl;
}

void configure_traffic_graph()
{
  // Crear grafo
  string * arcs_desc_reg = new string[4];
  string * arcs_desc_3 = new string[3];  
  create_street("Las Tapias", "Humboldt", 5, 20, "Albarregas");
  create_street("Las Tapias", "Pie del Llano", 10, 20, "Av. Andres Bello");
  create_street("Humboldt", "Santo Niño", 6, 20, "Av. Humboldt");
  create_street("Humboldt", "Santa Barbara", 11, 20, "Las Americas 1");
  
  arcs_desc_3[0] = "Santo Niño";
  arcs_desc_3[1] = "Las Tapias";
  arcs_desc_3[2] = "Santa Barbara";
  create_semaphore_agent (1, "Humboldt", 3, 1, arcs_desc_3);
  
  create_street("Santo Niño", "Casa Blanca", 20, 20, "Panamericana");
  create_street("Santa Barbara", "Mc Donalds", 8, 20, "Las Americas 2");
  create_street("Pie del Llano", "Santa Barbara", 7, 20, "Sucre");

  arcs_desc_3[0] = "Mc Donalds";
  arcs_desc_3[1] = "Humboldt";
  arcs_desc_3[2] = "Pie del Llano";
  create_semaphore_agent (2, "Santa Barbara", 3, 2, arcs_desc_3);
  
  create_street("Pie del Llano", "Santa Juana", 5, 20, "16 de Septiembre 1");
  create_street("Santa Juana", "Medicina", 16, 20, "16 de Septiembre 2");
  create_street("Medicina", "Glorias Patrias", 4, 20, "Miranda 1");
  create_street("Pie del Llano", "Glorias Patrias", 15, 20, "Av. Urdaneta");
  
  arcs_desc_reg[0] = "Las Tapias";
  arcs_desc_reg[1] = "Santa Juana";
  arcs_desc_reg[2] = "Glorias Patrias";
  arcs_desc_reg[3] = "Santa Barbara";
  create_semaphore_agent (3, "Pie del Llano", 4, 3, arcs_desc_reg);
  
  create_street("Glorias Patrias", "Mercado", 6, 20, "Miranda 2");

  arcs_desc_3[0] = "Medicina";
  arcs_desc_3[1] = "Mercado";
  arcs_desc_3[2] = "Pie del Llano";
  create_semaphore_agent (4, "Glorias Patrias", 3, 1, arcs_desc_3);

  
  create_street("Mc Donalds", "Mercado", 7, 20, "Las Americas 3");
  create_street("Mc Donalds", "Casa Blanca", 3, 20, "El Rodeo");
  
  arcs_desc_3[0] = "Santa Barbara";
  arcs_desc_3[1] = "Mercado";
  arcs_desc_3[2] = "Casa Blanca";
  create_semaphore_agent (5, "Mc Donalds", 3, 2, arcs_desc_3);
  
  create_street("Casa Blanca", "Alto Prado", 14, 20, "Los Proceres");
  
  arcs_desc_3[0] = "Alto Prado";
  arcs_desc_3[1] = "Santo Niño";
  arcs_desc_3[2] = "Mc Donalds";
  create_semaphore_agent (6, "Casa Blanca", 3, 2, arcs_desc_3);
  
  create_street("Mercado", "Viaducto", 7, 20, "Las Americas 4");

  arcs_desc_3[0] = "Viaducto";
  arcs_desc_3[1] = "Mc Donalds";
  arcs_desc_3[2] = "Glorias Patrias";
  create_semaphore_agent (7, "Mercado", 3, 3, arcs_desc_3);

  create_street("Viaducto", "Alto Prado", 6, 20, "Campo Elias");
  
}

void vehicle_situation(void * agent_impl)
{
	Traffic_Vehicle * vehicle = static_cast<Traffic_Vehicle *>(agent_impl);
	cout << "Tiempo " << vehicle->get_agent().actual_time << ": Atendiendo evento en vehiculo " << vehicle->get_description() << endl;
	switch(vehicle->get_last_event())
	{
		case Agent_Creation:
      cout << "Evento: Creación de agente" << endl;
			break;
		case Agent_Destruction:
      cout << "Evento: Destrucción de agente" << endl;
			break;
		case Node_Arrival:
      cout << "Evento: Llegada a nodo" << endl;
			break;
		case Node_Departure:
      cout << "Evento: Salida de nodo" << endl;
			break;
    case Semaphore_Check:
      cout << "Evento: Chequeo de semáforo" << endl;
      break;
	}
	if (vehicle->get_agent().in_node){
		Traffic_Node * node = static_cast<Traffic_Node *>(vehicle->get_agent().location);
    cout << "Ubicado en Nodo " << node->get_info().description << endl;
	}	else {
    Traffic_Arc * arc = static_cast<Traffic_Arc *>(vehicle->get_agent().location);
    cout << "Ubicado en Arco " << arc->get_info().description << endl;
	}
  if (vehicle->is_waiting_for_semaphore())
  {
    cout << "Está esperando que cambie el semáforo" << endl;
  }
  if (vehicle->is_waiting_destruction())
  {
    cout << "Está esperando por ser destruido" << endl;
  }
}

void create_agents_in_graph()
{
  cout<<"Creando los agentes en el Grafo"<<endl;
  for (int i = 1; i <= 5; i++) 
  {
 		stringstream agent_name_strm(stringstream::out) ;
    agent_name_strm << "Agente " << i;
    string agent_name =  agent_name_strm.str();
    Traffic_Random_Vehicle *vehicle = new Traffic_Random_Vehicle(i, agent_name);
    int index = rand() % 13;
    vehicle->set_update_callback(&vehicle_situation, vehicle);
    Traffic_Graph::Node * n1 = graph->search_node(Traffic_Node_Info(nodos[index]));
    Traffic_Agent_Impl::create_agent_in_node(vehicle, graph, n1, 10 + index);
  }
}

Traffic_Agent_Impl * create_vehicle(long id)
{
    stringstream agent_name_strm(stringstream::out) ;
    agent_name_strm << "Agente " << id;
    string agent_name =  agent_name_strm.str();
    Traffic_Random_Vehicle *vehicle = new Traffic_Random_Vehicle(id, agent_name);
    return vehicle;
}

void assign_vehicle_renderer(Traffic_Agent_Impl * agent_impl)
{
  agent_impl->set_update_callback(&vehicle_situation, agent_impl);
}

void program_agent_creation()
{
  Random_Time * rtime = new Random_Time(Exponential, 5);
  Traffic_Agent_Creation_Event * creation_event = 
    new Traffic_Agent_Creation_Event(&create_vehicle, rtime, 10);
  creation_event->set_mode(Probabilistic_Mode);
  creation_event->set_stop_probability(5);
  creation_event->set_renderer_callback(&assign_vehicle_renderer);
  creation_event->set_next_agent_id(1);
  creation_event->set_occurrence_mean(15);
  graph->schedule_event(creation_event);
}

int main()
{
	srand((unsigned)time(0));
  graph = new Traffic_Graph(1);
  configure_traffic_graph();
  program_agent_creation();
 
  // Comenzar la ejecución
  //graph->start_graph();
  graph->run_static();
  
  // Permitir que se ejecute por 10 segundos
  //sleep(10);
  //graph->stop_graph(); 
  
  delete graph;

}
